package ldh.im.fxbase.dao;

import ldh.im.fxbase.data.pojo.ImGroup;
import ldh.im.fxbase.data.pojo.ImSession;
import ldh.im.fxbase.data.pojo.ext.ImSessionExt;
import ldh.im.fxbase.data.pojo.status.ImSessionStatus;
import ldh.im.util.DateUtil;
import ldh.im.fxbase.util.DbUtil;
import ldh.im.util.ConfigUtil;
import ldh.rpc.util.JsonUtil;
import org.apache.commons.dbutils.ResultSetHandler;
import org.apache.commons.dbutils.handlers.ArrayListHandler;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.SQLException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

public class ImSessionDao extends BaseDao {

    private static final Logger LOGGER = LoggerFactory.getLogger(ImSessionDao.class);

    private static final String TABLE_NAME = "im_session";
    private static final String SESSION_COLUMNS = "s.id, s.name, s.serverId, s.currentUserId, s.fromUserId, s.toUserId, s.groupCode, s.sessionType sessionTypeValue, " +
            "s.hotMessageTotal, s.latelyMessage, s.status statusValue, s.isWaking waking, s.isTop top, s.isDisturb disturb, s.isWaking2 waking2, s.isTop2 top2, " +
            "s.isDisturb2 disturb2, s.ext1, s.ext2, s.createTime, s.updateTime";

    private ImGroupDao imGroupDao = new ImGroupDao();

    public List<ImSession> getByUserIdAndSize(String userId, int size) throws SQLException {
        int preDay = ConfigUtil.getInt("message.session.showDay", -14);
        LocalDateTime localDateTime = LocalDateTime.now().plusDays(preDay);
        return getByUserIdAndTimeAndSize(userId, localDateTime, 0, size);
    }

    public List<ImSession> getByUserIdAndTimeAndSize(String userId, LocalDateTime localDateTime, int start, int size) throws SQLException {
        return DbUtil.query((queryRunner, connection) -> {
            String sql = String.format("select %s from im_session s where s.currentUserId=? and (s.status not in(2)) and s.updateTime > ? " +
                    "order by s.hotMessageTotal desc, s.updateTime desc limit %s, %s", SESSION_COLUMNS, start, size);
            String startDay = localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            LOGGER.info("getMessageSessions sql: {}, userId: {}, startDay: {}", sql, userId, startDay);
            List<ImSessionExt> imSessionList = queryRunner.query(connection, sql, new BeanListHandler<ImSessionExt>(ImSessionExt.class), userId, startDay);
            for(ImSession imSession : imSessionList) {
                ImGroup imGroup = imGroupDao.getByUserIdAndId(userId, imSession.getGroupCode());
                imSession.setImGroup(imGroup);
            }
            return imSessionList.stream().map(s->s).collect(Collectors.toList());
        });
    }

    public void insertSession(ImSession imSession) throws SQLException {
        DbUtil.transaction((queryRunner, connection) -> {
            ResultSetHandler<Long> h = new ScalarHandler();
            String sql = String.format("insert into im_session(currentUserId, name, serverId, fromUserId, toUserId, sessionType, groupCode, hotMessageTotal, latelyMessage," +
                    "isWaking, isTop, isDisturb, isWaking2, isTop2, isDisturb2, status, createTime, updateTime) values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
            long n = queryRunner.insert(connection, sql, h, imSession.getCurrentUserId(), imSession.getName(), imSession.getServerId(), imSession.getFromUserId(),
                    imSession.getToUserId(), valueEnum(imSession.getSessionType()), imSession.getGroupCode(), imSession.getHotMessageTotal(), imSession.getLatelyMessage(),
                    imSession.isWaking(), imSession.isTop(), imSession.isDisturb(), imSession.isWaking2(), imSession.isTop2(), imSession.isDisturb2(),
                    valueEnum(imSession.getStatus()), imSession.getCreateTime(), imSession.getUpdateTime());
            imSession.setId(n);

            return n;
        });
    }

    public void batchInserts(List<ImSession> imSessionList) throws SQLException {
        DbUtil.transaction((queryRunner, connection) -> {
            Object[][] paras = imSessionList.stream().map(s->new Object[]{s.getCurrentUserId(), s.getName(), s.getServerId(), s.getFromUserId(), s.getToUserId(), valueEnum(s.getSessionType()),
                    s.getGroupCode(), s.getHotMessageTotal(), s.getLatelyMessage(), s.isWaking(), s.isTop(), s.isDisturb(), s.isWaking2(), s.isTop2(), s.isDisturb2(),
                    valueEnum(s.getStatus()), s.getCreateTime(), s.getUpdateTime()}).toArray(Object[][]::new);
            String sql = String.format("insert into im_session(currentUserId, name, serverId, fromUserId, toUserId, sessionType, groupCode, hotMessageTotal, latelyMessage, " +
                    "isWaking, isTop, isDisturb, isWaking2, isTop2, isDisturb2, status, createTime, updateTime) values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
            List<Object[]> results = queryRunner.insertBatch(connection, sql, new ArrayListHandler(), paras);
            for(int i=0; i<results.size(); i++) {
                Long bigInteger = (Long) results.get(i)[0];
                imSessionList.get(i).setId(bigInteger.longValue());
            }
            return imSessionList;
        });
    }

    public void updateSession(ImSession ms) throws SQLException {
        DbUtil.transaction((queryRunner, connection) -> {
            String sql = String.format("update im_session set name=?, serverId = ?, latelyMessage = ?, hotMessageTotal = ?, isWaking = ?, isTop = ?, isDisturb = ?, " +
                    "isWaking2 = ?, isTop2 = ?, isDisturb2 = ?, updateTime = ?, status=? where id = ?");
            long n = queryRunner.update(connection, sql, ms.getName(), ms.getServerId(), ms.getLatelyMessage(), ms.getHotMessageTotal(), ms.isWaking(), ms.isTop(),
                    ms.isDisturb(), ms.isWaking2(),
                    ms.isTop2(), ms.isDisturb2(), ms.getUpdateTime(), valueEnum(ms.getStatus()), ms.getId());
            if (n != 1) {
                throw new RuntimeException("更新数据失败");
            }
            return n;
        });
    }

    public void updateServerId(ImSession ms) throws SQLException {
        DbUtil.transaction((queryRunner, connection) -> {
            long n = 0;
            if (ms.getGroupCode() != null) {
                String sql = String.format("update im_session set serverId = ?, updateTime = ? where currentUserId = ? and groupCode = ?");
                n = queryRunner.update(connection, sql, ms.getServerId(), ms.getUpdateTime(), ms.getFromUserId(), ms.getGroupCode());
                if (n != 1) {
                    throw new RuntimeException("更新数据失败");
                }
            } else {
                String sql = String.format("update im_session set serverId = ?, updateTime = ? where currentUserId = ? and fromUserId = ? and toUserId = ?");
                n = queryRunner.update(connection, sql, ms.getServerId(), ms.getUpdateTime(), ms.getFromUserId(), ms.getFromUserId(), ms.getToUserId());
                if (n != 1) {
                    throw new RuntimeException("更新数据失败");
                }
            }

            return n;
        });
    }

    public void batchUpdates(List<ImSession> sessions) throws SQLException {
        if (sessions.size() < 1) return;
        Object[][] paras = sessions.stream().map(ms-> new Object[] {ms.getServerId(), ms.getLatelyMessage(), ms.getHotMessageTotal(), ms.isWaking(), ms.isTop(), ms.isDisturb(),
                ms.isWaking2(), ms.isTop2(), ms.isDisturb2(), ms.getUpdateTime(), valueEnum(ms.getStatus()), ms.getId()}).toArray(Object[][]::new);
        DbUtil.transaction((queryRunner, connection) -> {
            String sql = String.format("update im_session set serverId = ?, latelyMessage = ?, hotMessageTotal = ?, isWaking = ?, isTop = ?, isDisturb = ?, " +
                    "isWaking2 = ?, isTop2 = ?, isDisturb2 = ?, updateTime = ?, status=? where id = ?");
            LOGGER.debug("batchUpdates sql: {}, data: {}", sql, JsonUtil.toJson(paras));
            int[] ns = queryRunner.batch(connection, sql, paras);
            if (ns.length != sessions.size()) {
                throw new RuntimeException("更新数据失败");
            }
            return ns.length;
        });
    }

    public void updateSessionGroupCode(ImSession ms) throws SQLException {
        DbUtil.transaction((queryRunner, connection) -> {
            String sql = String.format("update im_session set serverId = ?, groupCode = ?, updateTime = ?, status=? where id = ?");
            long n = queryRunner.update(connection, sql, ms.getServerId(), ms.getGroupCode(),  ms.getUpdateTime(), valueEnum(ms.getStatus()), ms.getId());
            if (n != 1) {
                throw new RuntimeException("更新数据失败");
            }
            return n;
        });
    }

    public ImSession getByUserId(String currentUserId, String fromUserId, String toUserId) throws SQLException {
        return DbUtil.transaction((queryRunner, connection) -> {
            String sql = String.format("select %s from im_session s where s.currentUserId = ? and ((s.fromUserId = ? and s.toUserId = ?) or (s.fromUserId = ? and s.toUserId = ?))", SESSION_COLUMNS);
            ImSessionExt imSession = queryRunner.query(connection, sql, new BeanHandler<>(ImSessionExt.class), currentUserId, fromUserId, toUserId, toUserId, fromUserId);
            initGroupForSession(currentUserId, imSession);
            return imSession;
        });
    }

    public ImSession getByGroupCodeAndUserId(String groupCode, String userId) throws SQLException {
        return DbUtil.transaction((queryRunner, connection) -> {
            String sql = String.format("select %s from im_session s where s.groupCode = ? and s. currentUserId = ?", SESSION_COLUMNS);
            ImSessionExt imSession = queryRunner.query(connection, sql, new BeanHandler<>(ImSessionExt.class), groupCode, userId);
            initGroupForSession(userId, imSession);
            return imSession;
        });
    }

    public ImSession getSessionById(Long id) throws SQLException {
        return DbUtil.transaction((queryRunner, connection) -> {
            String sql = String.format("select %s from im_session s where s.id = ?", SESSION_COLUMNS);
            ImSessionExt imSession = queryRunner.query(connection, sql, new BeanHandler<>(ImSessionExt.class), id);
            initGroupForSession(imSession.getFromUserId(), imSession);
            return imSession;
        });
    }

    public ImSession getSessionByServerId(String userId, String serverId) throws SQLException {
        return DbUtil.transaction((queryRunner, connection) -> {
            String sql = String.format("select %s from im_session s where s.currentUserId = ? and s.serverId = ?", SESSION_COLUMNS);
            ImSessionExt imSession = queryRunner.query(connection, sql, new BeanHandler<>(ImSessionExt.class), userId, serverId);
            initGroupForSession(imSession.getFromUserId(), imSession);
            return imSession;
        });
    }

    public List<ImSession> getSessionByServerIds(String userId, List<String> serverIds) throws SQLException {
        if (serverIds.size() < 1) return new ArrayList<>();
        String serverIdParam = serverIds.stream().map(s->String.format("'%s'", s)).collect(Collectors.joining(","));
        return DbUtil.transaction((queryRunner, connection) -> {
            String sql = String.format("select %s from im_session s where s.currentUserId = ? and s.serverId in (%s)", SESSION_COLUMNS, serverIdParam);
            List<ImSessionExt> imSessions = queryRunner.query(connection, sql, new BeanListHandler<>(ImSessionExt.class), userId);
            for (ImSession imSession : imSessions) {
                initGroupForSession(imSession.getFromUserId(), imSession);
            }
            return imSessions.stream().map(s->s).collect(Collectors.toList());
        });
    }

    public List<ImSession> getSessionByIds(List<Long> ids) throws SQLException {
        if (ids.size() < 1) return new ArrayList<>();
        String idParam = ids.stream().map(s->String.format("%s", s)).collect(Collectors.joining(","));
        return DbUtil.transaction((queryRunner, connection) -> {
            String sql = String.format("select %s from im_session s where s.id in (%s)", SESSION_COLUMNS, idParam);
            List<ImSessionExt> imSessions = queryRunner.query(connection, sql, new BeanListHandler<>(ImSessionExt.class));
            for (ImSession imSession : imSessions) {
                initGroupForSession(imSession.getFromUserId(), imSession);
            }
            return imSessions.stream().map(s->s).collect(Collectors.toList());
        });
    }

    public void cleanHotMessageNum(long id) throws SQLException {
        DbUtil.transaction((queryRunner, connection) -> {
            String sql = String.format("update im_session set hotMessageTotal = ? where id = ?");
            updateBeanById(queryRunner, connection, sql, 0, id);
            return null;
        });
    }

    public void setHotMessageNum(Long id, int value) throws SQLException {
        if (value < 0) return;
        DbUtil.transaction((queryRunner, connection) -> {
            String sql = String.format("update im_session set hotMessageTotal = ? where id = ?");
            updateBeanById(queryRunner, connection, sql, value, id);
            return null;
        });
    }

    public void incrHotMessageNum(Long sessionId, int hotMessageTotal) throws SQLException {
        DbUtil.transaction((queryRunner, connection) -> {
            String sql = String.format("update im_session set hotMessageTotal = hotMessageTotal + ?, updateTime=? where id = ?");

            LOGGER.debug("incrHotMessageNum sql: {}, data: {}, {}", sql, sessionId, hotMessageTotal);
            updateBeanById(queryRunner, connection, sql, hotMessageTotal, new Date(), sessionId);
            return null;
        });
    }

    public void updateMessageUpdateTimes(String currentUserId, Set<String> toUserIdSet) throws SQLException {
        DbUtil.transaction((queryRunner, connection) -> {
            for (String toUserId : toUserIdSet) {
                String sql = String.format("update im_session set updateTime=? where currentUserId = ? and ((fromUserId = ? and toUserId = ?) or (toUserId = ? and fromUserId = ?))");
                updateBeanById(queryRunner, connection, sql, DateUtil.formatH2(new Date()), currentUserId, currentUserId, toUserId, currentUserId, toUserId);
            }

            return null;
        });
    }

    public void changeMessageSessionGroupCode(List<ImGroup> updateImGroupList) throws SQLException {
        DbUtil.transaction((queryRunner, connection) -> {
            Object[][] paras = updateImGroupList.stream().map(g->new Object[]{g.getServerId(), valueEnum(ImSessionStatus.enable), new Date(), g.getCurrentUserId(), g.getId()+""}).toArray(Object[][]::new);

            String sql = String.format("update im_session ms set ms.groupCode = ?, ms.status = ?, ms.updateTime = ? where ms.currentUserId = ? and ms.groupCode = ? and ms.status = '2'");

            queryRunner.batch(sql, paras);
            return null;
        });
    }

    private void initGroupForSession(String userId, ImSession imSession) throws SQLException {
        if (imSession != null && imSession.getGroupCode() != null) {
            ImGroup imGroup = imGroupDao.getByUserIdAndId(userId, imSession.getGroupCode());
            imSession.setImGroup(imGroup);
        }
    }
}
